#include <net/if.h>
#include <sys/ioctl.h>
#include <cstdlib>
#include <cerrno>
#include <cstdio>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <iostream>
#include <sys/poll.h>
#include <sys/epoll.h>

#include "readConfig.h"
#include "signal.h"
#include "netDevice.h"
#include "unp.h"
#include "network.h"
#include "config.h"

using namespace std;

const char ConfigFile[]= "config";
static const int timeout = 1;
string netMode = "wifi";	//433M or wifi
string wirelessAddr;
map<string, char> addr2id;
map<char, string> id2addr;

void sig_chld(int signo)
{
	pid_t pid;
	int stat;
	while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
		cout<<"child "<<pid<<" terminated"<<endl;
	return;
}

void plat2Node(int sockfd, int nodeFd)
{
	char cmdBuff[65535];
	/* recv cmd from plat */
	int res;

	struct pollfd fds[2];
	fds[0].fd = sockfd;
	fds[0].events = POLLIN;

	fds[1].fd = nodeFd;
	fds[1].events = POLLIN;

	while(true)
	{
		bzero(cmdBuff, sizeof(cmdBuff));
		res = TEMP_FAILURE_RETRY(poll(fds, 2, -1));
		if(res == 0)
		{
			cout<<"Select timeout"<<endl;
			continue;
		}
		else if(res < 0)
		{
			perror("poll error");
			//exit(EXIT_FAILURE);
			return;
		}

		//Data from plat
		if((fds[0].revents & POLLIN))
		{
			res = TEMP_FAILURE_RETRY(recv(sockfd, cmdBuff, sizeof(cmdBuff), 0));
			cout<<"Gateway: Recv "<<res<<" bytes from plat"<<endl;
			if(res == 0)
			{
				cout<<"Plat close socket"<<endl;
				break;
			}
			else if(res < 0)
			{
				perror("Gateway: recv from plat error");
				//exit(EXIT_FAILURE);
				return;
			}
			else
			{
				res = TEMP_FAILURE_RETRY(send(nodeFd, cmdBuff, res, 0));
				if(res>0)
					cout<<"Send "<<res<<" bytes to node."<<endl;
				else if(res<0)
				{
					cout<<"Send to node error"<<endl;
					return;
				}
			}
		}//if((fds[0].revents & POLLIN))

		//Data from node
		if((fds[1].revents & POLLIN))
		{
			res = TEMP_FAILURE_RETRY(recv(nodeFd, cmdBuff, sizeof(cmdBuff), 0));
			cout<<"Gateway: Recv "<<res<<" bytes from node"<<endl;
			if(res > 0)
			{
				res = TEMP_FAILURE_RETRY(send(sockfd, cmdBuff, res, 0));
				if(res>0)
	        cout<<"Gateway: Send "<<res<<" bytes to plat."<<endl;
	      else if(res<0)
	      {
		      cout<<"Gateway: Send to plat error"<<endl;
		      return;
		    }
			}
			else if(res ==0)
			{
				cout<<"Gateway: Disconneted from node"<<endl;
				break;
			}
			else
			{
				perror("Gateway: Recv from node error");
				return;
			}
		}//if((fds[1].revents & POLLIN))
	}
}

void* server433(void* null)
{
	int res;
	tcpServer server;
	server.Listen(FTTMServerPort, "127.0.0.1");

	//cout<<"433M Server: Listen on port "<<ntohs(local.sin_port)<<endl;

	//Create 433M device
	FTTMDevice ndd;
	ndd.setup();

	sleep(1);
	tcpSocket client;
	client.Connect("", netDeviceListenPort);
	int cli433fd = client.getSocket();

	udpClient udpServer;
	udpServer.Bind("127.0.0.1", netMSCommandRPort);

	const int MAX_EVENTS = 100;
	struct epoll_event ev, events[MAX_EVENTS];
	int epollfd = epoll_create(MAX_EVENTS);
	if(epollfd == -1)
	{
		perror("433M server epoll_create");
		exit(-1);
	}

	ev.events = EPOLLIN;
	ev.data.fd = server.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: server.getSocket()");
		exit(-1);
	}

	ev.events = EPOLLIN;
	ev.data.fd = cli433fd;
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: cli433fd");
		exit(-1);
	}

	ev.events = EPOLLIN;
	ev.data.fd = udpServer.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: udpServer.getSocket()");
		exit(-1);
	}

	char buff[254];

	int connfd, readn, nfds;

	struct sockaddr_in cliaddr;
	socklen_t slen = sizeof(cliaddr);
	char paddr[INET_ADDRSTRLEN];

	map<unsigned char, int> id;

	while(true)
	{
		nfds = TEMP_FAILURE_RETRY(epoll_wait(epollfd, events, MAX_EVENTS, -1));
		if(nfds == -1)
		{
			perror("epoll_wait");
			continue;	
			//exit(-1);
		}

		for(int i=0; i<nfds; ++i)
		{
			//Connect from gateway
			if(events[i].data.fd == server.getSocket())
			{
				connfd = TEMP_FAILURE_RETRY(accept(events[i].data.fd, (SA*)&cliaddr, &slen));
				if(connfd < 0)
				{
					perror("accept error");
					exit(EXIT_FAILURE);
				}
				cout<<"433M Server: Receive connect from: "<<inet_ntop(AF_INET, &cliaddr.sin_addr, paddr, INET_ADDRSTRLEN)<<":"<<ntohs(cliaddr.sin_port)<<endl;

				//New connection
				ev.events = EPOLLIN;
				ev.data.fd = connfd;
				res = TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev));

				//Read and save id
				char initial;
				Readn(connfd, &initial, 1);	//Read ID
				id[initial] = connfd;	//ID
				//cout<<"433M server: ID is "<<(int)rid<<endl;
			}
			//Data from 433M device
			else if(events[i].data.fd == cli433fd)
			{
				cout<<"433M server: Data from 433M device"<<endl;
				char len;
				char buff[22];
 
				res = Readn(cli433fd, &len, 1);
				if(0 == res)
				{
					Close(cli433fd);

					//Delete from epoll
					TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_DEL, cli433fd, &ev));
					//Delete from id map
					map<unsigned char, int>::iterator map_it = id.begin();
					while(map_it != id.end())
					{
						if(map_it->second == cli433fd)
							id.erase(map_it);
						++map_it;
					}

					continue;
				}

				Readn(cli433fd, buff, len);

				if(buff[1] == DFLAG)
				{
					if(id.count(buff[0]))
					{
						cout<<"server433: Send "<<len - 2<<" bytes to gateway"<<endl;
						Send(id[buff[0]], buff + 2, len - 2, 0); //Send data to gateway
					}
					else
						cout<<"No such id"<<endl;
				}
				else if(buff[1] == CFLAG)
				{
					cout<<"Control Msg"<<endl;
					buff[4] = buff[0];
					cout<<"server433: Send Conrol msg to NetM"<<endl;
					udpServer.Sendto(buff+3, 2, "", networkMPort);
				}
			}
			else if(events[i].data.fd == udpServer.getSocket())
			{
				char buff[100];
				short unsigned int remotePort;
				string remoteAddr;
				udpServer.Recvfrom((char *)buff, sizeof(buff), remoteAddr, remotePort);

				if(remoteAddr == "127.0.0.1")
				{
					if(buff[0] == turnOnWiFi)
					{
						char sbuff[100];
						bzero(sbuff, sizeof(sbuff));
						sbuff[0] = 4 + wirelessAddr.size();
						sbuff[1] = 0;
						sbuff[2] = CFLAG;
						sbuff[3] = turnOnWiFi;
						strcpy(sbuff + 4, wirelessAddr.c_str());

						Send(cli433fd, sbuff, 5 + wirelessAddr.size(), 0);
					}
					else if(buff[0] == turnOffWiFi)
					{
						char sbuff[] = {0x03, buff[1], CFLAG, turnOffWiFi};
						Send(cli433fd, sbuff, sizeof(sbuff), 0);
					}
					else if(buff[0] == turnOn433)
					{
						ndd.on();
					}
					else if(buff[0] == turnOff433)
					{
						//ndd.off();
						cout<<"ndd.off"<<endl;
					}
				}
			}
			//Data from gateway
			else
			{
				cout<<"433M server: Data from gateway"<<endl;
				int currfd = events[i].data.fd;
				readn = Read(currfd, buff + 3, sizeof(buff) - 3);

				map<unsigned char, int>::iterator map_it = id.begin();
				while(map_it != id.end())
				{
					if(map_it->second == currfd)
						break;
					++map_it;
				}

				if(0 == readn)
				{
					Close(currfd);

					//Delete from epoll
					TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_DEL, currfd, &ev));
					//Delete from id map
					id.erase(map_it);
					continue;
				}

				buff[0] = (char)readn + 2;	//Send length
				buff[1] = map_it->first;	//ID
				buff[2] = DFLAG;	//ADD FLAG
				Send(cli433fd, buff, readn + 3, 0);	//Send data

				cout<<"433M server: Send "<<readn<<" bytes(id<1 byte> + data) to 433M device id: "<<(int)(map_it->first)<<endl;
			}//else
		}//for
	}//while

	return (void*)NULL;
}

void *netM(void *null)
{
	srand(time(0));
	tcpServer server;
	server.Listen(fromManagePlatM);

	udpClient udpServer;
	udpServer.Bind("", networkMPort);
	
	tcpServer netMServer;
	netMServer.Listen(networkMPortTCP);
	
	map<char, char> nodeStatus;
	
	const int MAX_EVENTS = 30;
	struct epoll_event ev, events[MAX_EVENTS];
	int epollfd = epoll_create(MAX_EVENTS);
	if(epollfd == -1)
	{
		perror("epoll_create");
		exit(-1);
	}

	//ADD control for tcp server
	ev.events = EPOLLIN;
	ev.data.fd = netMServer.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: netMServer.getsocket()");
		exit(-1);
	}
	
	//ADD control for tcp server
	ev.events = EPOLLIN;
	ev.data.fd = server.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: server.getsocket()");
		exit(-1);
	}

	//Add control for udp server
	ev.events = EPOLLIN;
	ev.data.fd = udpServer.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: udpServer.getsocket()");
		exit(-1);
	}

	int nfds;
	int connfd;
	while(true)
	{
		nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
		if(nfds == -1)
			break;
		for(int i = 0;i<nfds;i++)
		{
			if(events[i].data.fd == server.getSocket())
			{
				struct sockaddr_in cliaddr;
				socklen_t clilen = sizeof(cliaddr);
				connfd = TEMP_FAILURE_RETRY(accept(server.getSocket(), (struct sockaddr *)&cliaddr, &clilen));
				char paddr[INET_ADDRSTRLEN];
				cout<<"NetM: Receive connect from : "<<inet_ntop(AF_INET, &cliaddr.sin_addr, paddr, INET_ADDRSTRLEN)<<":"<<ntohs(cliaddr.sin_port)<<endl;
				ev.data.fd = connfd;
				if(epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev))
				{
					perror("epoll_ctl: server.epoll_ctl()");
					exit(1);
				}
			}
			else if(events[i].data.fd == netMServer.getSocket())
			{
				struct sockaddr_in cliaddr;
				socklen_t clilen = sizeof(cliaddr);
				connfd = TEMP_FAILURE_RETRY(accept(netMServer.getSocket(), (struct sockaddr *)&cliaddr, &clilen));
				char paddr[INET_ADDRSTRLEN];
				cout<<"NetM: Receive connect from : "<<inet_ntop(AF_INET, &cliaddr.sin_addr, paddr, INET_ADDRSTRLEN)<<":"<<ntohs(cliaddr.sin_port)<<endl;
				ev.data.fd = connfd;
				if(epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev))
				{
					perror("epoll_ctl: netMServer.epoll_ctl()");
					exit(1);
				}
				
				nodeStatus[addr2id[string(paddr)]] = 0;
			}
			else if(events[i].data.fd == udpServer.getSocket())
			{
				string remoteAddr;
				short unsigned remotePort;
				char buff[1000];
				udpServer.Recvfrom((char *)buff, sizeof(buff), remoteAddr, remotePort);	
				//DBUG
				/*
				cout<<"--------------------"<<endl;
				cout<<"NetM: UDP server receive from "<<remoteAddr<<endl;
				for(int i=0;i<res;++i)
					printf("%02hhx ", buff[i]);
				cout<<endl
				    <<"+++++++++++++++++++++"<<endl;
				*/
				switch(buff[0])
				{
					case turnOn433Success:
						cout<<"NetM: ID "<<(int)buff[1]<<" turn on 433M Success"<<endl;
						buff[0] = turnOffWiFi;
						udpServer.Sendto(buff, 2, "", netMSCommandRPort);
						break;
					case turnOffWiFiSuccess:
						cout<<"NetM: ID "<<(int)buff[1]<<" turn off WiFi Success"<<endl;
						break;
					case turnOn433:
						break;
					default:
						printf("%02hhx\n", buff[0]);
						cout<<"NetM: Receive Error Argument"<<endl;
				}
			}
			else
			{
				int res;
				char len;
				char buff[1000];
				int sock = events[i].data.fd;
				struct sockaddr_in addr;
				char paddr[INET_ADDRSTRLEN];
				socklen_t length_ptr;
				getpeername(sock, (struct sockaddr *)&addr, &length_ptr);				
				inet_ntop(AF_INET, &addr.sin_addr, paddr, INET_ADDRSTRLEN);
				if(addr2id.count(paddr))
				{
					res = Readn(sock, &len, 1);
					cout<<"res: "<<res<<endl;
					if(0 == res)
					{
						cout<<"close1"<<endl;
						Close(sock);

						//Delete from epoll
						TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_DEL, sock, &ev));
						
						nodeStatus.erase(addr2id[paddr]);
					}
					else
					{
						Readn(sock, buff, len);
						char ACK[] = {0x01, ack};
						if(buff[0] == turnOnWiFiSuccess)
						{
							cout<<"Send ACK"<<endl;
							Send(sock, ACK, sizeof(ACK), 0);
							cout<<"Send ACK OK"<<endl;
							cout<<"NetM: ID "<<(int)addr2id[paddr]<<" turn on wifi Success"<<endl;
							char sbuff[] = {0x01, turnOff433};
							Send(sock, sbuff, sizeof(buff), 0);
							cout<<"broken?"<<endl;
						}
						else if(buff[0] == turnOff433Success)
						{
							cout<<"Send ACK"<<endl;
							Send(sock, ACK, sizeof(ACK), 0);
							cout<<"Send ACK OK"<<endl;
							cout<<"NetM: ID "<<(int)addr2id[paddr]<<" turn off 433M Success"<<endl;
						}
						
						else if(buff[0] == ack)
							continue;
					}
					
					continue;
				}	
								
				//Cmd from plat
				res = Readn(sock, &len, 1);
				if(0 == res)
				{
					cout<<"close2"<<endl;
					Close(sock);

					//Delete from epoll
					TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_DEL, sock, &ev));
				}
				else
				{
					Readn(sock, buff, len);
					if(buff[0] == changeTo433)
					{
						cout<<"NetM: Change to 433"<<endl;
						if(netMode == "433M")
						{	
							cout<<"NetM: 433M now"<<endl;
							continue;		
						}

						char sbuff[2];
						sbuff[0] = turnOn433;
						sbuff[1] = (char)rand();
						udpServer.broadcast(sbuff, sizeof(sbuff), wirelessInterface, networkMPort);
						udpServer.Sendto(&turnOn433, 1, "", netMSCommandRPort);
						//udpServer.Sendto(sbuff, sizeof(sbuff), string("192.168.2.152"), networkMPort);
						//system("sh rt73usb_rm.sh");
						netMode = "433M";
					}
					else if(buff[0] == changeToWiFi)
					{
						cout<<"NetM: Change to WiFi"<<endl;
						if(netMode == "wifi")
						{
							cout<<"NetM: WiFi now"<<endl;
							continue;
						}

						udpServer.Sendto(&turnOnWiFi, 1, "", netMSCommandRPort);
						udpServer.Sendto(&turnOff433, 1, "", netMSCommandRPort);
						//system("sh rt73usb.sh");
						netMode = "wifi";
					}
					else if(buff[0] == getNetStatus)
					{
						cout<<"NetM: Get net status"<<endl;
						char sbuff[2];
						sbuff[0] = 1;
						if(netMode == "wifi")
							sbuff[1] = statusWifi;
						else if(netMode == "433M")
							sbuff[1] = status433;

						Send(sock, sbuff, sizeof(sbuff), 0);
					}
					else
					{
						cout<<"NetM: Error argument"<<endl;
					}
				}
			}
		}
	}
	return (void*)NULL;
}

int main(int argc, char** argv)
{
	Signal(SIGCHLD, sig_chld);
	/*
	 * Read id and ip address pair
	 */
	Config addrConfigFile("wifiAddr.conf");

	string addrString = "";
	char idStr[4];

	for(int i=0;i<256;++i)
	{
		sprintf(idStr, "%d", i);
		addrString = addrConfigFile.Read(idStr, string(""));
		//cout<<addrString<<endl;
		if(addrString != "")
		{
			id2addr[i] = addrString;
			addr2id[addrString] = i;
		}
	}

	/*
	 * Set up rt73 usb wifi card
	 */
	system("sh rt73usb.sh");

	/*
	 * Get local wireless interface address
	 */
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	struct ifreq ifr;

	bzero(&ifr, sizeof(ifr));
	strcpy(ifr.ifr_name, wirelessInterface.c_str());

	if(-1 == ioctl(sockfd, SIOCGIFADDR, &ifr))
	{
		perror("udpClient::broadcast ioctl()");
		exit(-1);
	}

	char paddr[INET_ADDRSTRLEN];
	inet_ntop(AF_INET, &((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr, paddr, INET_ADDRSTRLEN);
	wirelessAddr = paddr;
	cout<<"Gateway: local wireless address "<<wirelessAddr<<endl;

	/*
	 * Start 433M server
	 */
	pthread_t thread433Server;
	Pthread_create(&thread433Server, NULL, server433, NULL);

	/*
	 * Set up net management
	 */
	pthread_t threadnetM;
	Pthread_create(&threadnetM, NULL, netM, NULL);

	/*
	 * Set up TCP server for receive cmd from gateway
	 */
	tcpServer server;
	server.Listen(fromMagagePlat);
	int fd = server.getSocket();

	cout<<"Gateway: Listen on port "<<fromMagagePlat<<endl;

	while(true)
	{
		/*
		 * Accept connect from plat
		 */
		struct sockaddr_in cliaddr;
		socklen_t clilen = sizeof(cliaddr);
		int connfd = TEMP_FAILURE_RETRY(accept(fd, (struct sockaddr *)&cliaddr, &clilen));
		char paddr[INET_ADDRSTRLEN];
		cout<<"Gateway: Receive connect from : "<<inet_ntop(AF_INET, &cliaddr.sin_addr, paddr, INET_ADDRSTRLEN)<<":"<<ntohs(cliaddr.sin_port)<<endl;

		/*
		 * Fork a process for handle a connect
		 */
		if(Fork() == 0)  //in sub process
		{
			cout<<"new process"<<endl;
			Close(fd);

			/*
			 * Get node id will connect to
			 */
			//change by leon
			unsigned char id;

			Recv(connfd, &id, 1, 0);
			//change by leon end
			//cout<<"ID:"<<(unsigned char)id<<endl;
			printf("ID: %d\n", id);

			/*
			 * Build "node" address
			 */
			struct sockaddr_in nodeAddr;
			bzero(&nodeAddr, sizeof(nodeAddr));
			nodeAddr.sin_family = AF_INET;
			if(netMode == "wifi")
			{
				//port
				nodeAddr.sin_port = htons(cmdPort);

				if(id2addr.count(id))
				{
					inet_pton(AF_INET, id2addr[id].c_str(), &nodeAddr.sin_addr);
				}

			}
			else if(netMode == "433M")
			{
				nodeAddr.sin_port = htons(FTTMServerPort);
				nodeAddr.sin_addr.s_addr = htons(INADDR_ANY);
			}
			else
			{
				cout<<netMode<<" is a bad net mode"<<endl;
				exit(1);
			}

			/*
			 * Connect to node
			 */
			int nodefd = Socket(AF_INET, SOCK_STREAM, 0);
			if(connect(nodefd, (SA*)&nodeAddr, sizeof(nodeAddr)) < 0)
			{
				perror("connect to node error");
				goto exit_close;
			}
			char paddr[INET_ADDRSTRLEN];
			cout<<"Gateway: Connected to "<<inet_ntop(AF_INET, &nodeAddr.sin_addr, paddr, INET_ADDRSTRLEN)<<":"<<ntohs(nodeAddr.sin_port)<<endl;

			/*
			 * In 433M mode, send id to FTTMServer
			 */
			if(netMode == "433M")	//Tell 433M server which node does this socket wanted send to
				Send(nodefd, &id, 1, 0);

			/*
			 * Transmit data between plat and node
			 */
			plat2Node(connfd, nodefd);

exit_close:
			Close(nodefd);
			Close(connfd);
			exit(EXIT_SUCCESS);
		}//if((pid = fork()) == 0)
		Close(connfd);
	}//end while(true)
	Close(fd); //close listen fd

	return 0;
}//end main
